package com.liudachu.interview.SQL;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

/**
 * 1.什么叫数据库事物
 *  事物：一组逻辑操作单元，使数据从一种状态转换到另一种状态。
 *      -->一组逻辑操作单元：一个或多个DML操作
 * 2.事物处理
 *  事务处理是将多个操作或者命令一起执行，所有命令全部成功执行才意味着该事务的成功，任何一个命令失败都意味着该事务的失败。
 * 3.以银行转账为例（100块都不给），
 *      A要给B 转账100元，
 *      A转账的指令已经成功发出，而B 由于未知的原因接收失败，
 *      如果两个命令单独执行，那么A账户少了100块，但是B又没收到100块，显而易见是不合理的；
 *      如果将A向B 转账100元当成一个事务处理，那么由于B 接收的失败，整个转账事务都将失败，A不会少100，B更不会增加100，这个时候“100块都不给”才是合理的情况。
 * 4.数据一旦提交，就不可回滚
 *      a. DDL操作一旦执行，都会自动提交
 *      b. AML默认情况下，一旦执行，就会自动提交
 *          -->可以设置set automint = false 取消DML操作的自动提交
 *      c. 默认在关闭连接时，会自动提交数据
 * @author liudachu
 * @date 2021/9/25
 */
public class transactionTest {
    /**
     * 针对与数据表user——table来说
     * AA用户给BB用户转载100
     *
     * update user_table set balance = balance - 100 where user = 'AA'
     * update user_table set balance = balance + 100 where user = 'BB'
     *
     */
    public static void main(String[] args) {

        // TODO: 2021/9/25  模拟转账异常
//
//        String sql1 = "update user_table set balance = balance - 100 where user = ?";
//        update(sql1, "AA");
//
//        //模拟网络异常
//        System.out.println(10/0);
//
//        String sql2 = "update user_table set balance = balance + 100 where user = ?";
//        update(sql2, "BB");
//
//        System.out.println("转账成功");


        Connection conn = null;
        try {
            conn = JDBCUtils.getConnection();
            System.out.println("默认：" + conn.getAutoCommit());
            //取消数据的自动提交功能
            conn.setAutoCommit(false);

            String sql1 = "update user_table set balance = balance - 100 where user = ?";
            update1(conn, sql1, "AA");

//        //模拟网络异常
//        System.out.println(10/0);

            String sql2 = "update user_table set balance = balance + 100 where user = ?";
            update1(conn, sql2, "BB");

            System.out.println("转账成功");

            //提交数据
            conn.commit();

        } catch (Exception e) {
            e.printStackTrace();
            //回滚数据
            try {
                conn.rollback();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }finally {
            JDBCUtils.close(null,conn);
        }




    }

    //************************考虑数据库事物处理后的转账操作************************
    //考虑事物处理后的操作
    public static int update1(Connection conn, String sql, Object... args) {//sql中占位符的个数应该与可变形参的长度一致

        PreparedStatement ps = null;
        try {

            //1.预编译sql语句，返回PrepareStatement的实例
            ps = conn.prepareStatement(sql);

            //2.填充占位符
            for (int i = 0; i < args.length; i++) {
                ps.setObject(i + 1, args[i]);
            }

            //3.执行
            return ps.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //5.资源的关闭
            JDBCUtils.close(null, conn);
        }

        return 0;


    }
    //通用的增删改操作  未考虑事物处理的操作
    public static int update(String sql, Object... args) {//sql中占位符的个数应该与可变形参的长度一致
        Connection conn = null;
        PreparedStatement ps = null;
        try {
            //1.获取数据库的连接
            conn = JDBCUtils.getConnection();

            //2.预编译sql语句，返回PrepareStatement的实例
            ps = conn.prepareStatement(sql);

            //3.填充占位符
            for (int i = 0; i < args.length; i++) {
                ps.setObject(i + 1, args[i]);
            }

            //4.执行
            return ps.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //修改
            try {
                conn.setAutoCommit(true);
            }catch (SQLException e) {
                 e.printStackTrace();
            }
            //5.资源的关闭
            JDBCUtils.close(ps, conn);
        }

        return 0;


    }
}

